package database

import (
	"JoRedis/interface/reply"
	"JoRedis/protocol"
	"JoRedis/util"
)

// set k v
func setCmd(dbPtr *DB, argBytesSlc [][]byte) reply.ReplyItf {
	key := string(argBytesSlc[0])
	value := argBytesSlc[1]
	dbPtr.dictPtr.Put(key, value)
	return protocol.MakeOkReply()
}

// setnx k v
func setNxCmd(dbPtr *DB, argBytesSlc [][]byte) reply.ReplyItf {
	key := string(argBytesSlc[0])
	rst := dbPtr.dictPtr.PutIfExists(key, argBytesSlc[1])
	return protocol.MakeIntReply(int64(rst))
}

// mset k v k v k v
func mSetCmd(dbPtr *DB, argBytesSlc [][]byte) reply.ReplyItf {
	dataLen := len(argBytesSlc)
	for i := 0; i < dataLen/2; i++ {
		dbPtr.dictPtr.Put(string(argBytesSlc[i]), argBytesSlc[i+1])
	}
	return protocol.MakeOkReply()
}

// append k v
func appendCmd(dbPtr *DB, argBytesSlc [][]byte) reply.ReplyItf {
	key := string(argBytesSlc[0])
	value, exist := dbPtr.dictPtr.Get(key)
	bytes := value.([]byte)
	if exist {
		bytes = append(bytes, argBytesSlc[1]...)
	} else {
		bytes = argBytesSlc[1]
	}
	dbPtr.dictPtr.Put(key, bytes)
	return protocol.MakeOkReply()
}

// get k
func getCmd(dbPtr *DB, argBytesSlc [][]byte) reply.ReplyItf {
	key := string(argBytesSlc[0])
	value, exist := dbPtr.dictPtr.Get(key)
	if !exist {
		return protocol.MakeNullBulkReply()
	}
	valBytes, ok := value.([]byte)
	if !ok {
		return protocol.MakeWrongReply()
	}
	return protocol.MakeBulkReply(valBytes)
}

// mget k k
func mGetCmd(dbPtr *DB, argBytesSlc [][]byte) reply.ReplyItf {
	keyLen := len(argBytesSlc)
	rstBytesSlc := make([][]byte, keyLen)
	for i := 0; i < keyLen; i++ {
		key := string(argBytesSlc[i])
		value, exist := dbPtr.dictPtr.Get(key)
		if exist {
			rstBytesSlc[i] = value.([]byte)
		} else {
			rstBytesSlc[i] = protocol.MakeNullBulkReply().ToBytes()
		}
	}
	return protocol.MakeMultiBulkReply(rstBytesSlc)
}

// getset k v
func getSetCmd(dbPtr *DB, argBytesSlc [][]byte) reply.ReplyItf {
	key := string(argBytesSlc[0])
	value, exist := dbPtr.dictPtr.Get(key)
	if exist {
		return protocol.MakeBulkReply(value.([]byte))
	} else {
		return protocol.MakeNullBulkReply()
	}
}

// strlen ke
func strLenCmd(dbPtr *DB, argBytesSlc [][]byte) reply.ReplyItf {
	key := string(argBytesSlc[0])
	value, exist := dbPtr.dictPtr.Get(key)
	if exist {
		return protocol.MakeBulkReply(value.([]byte))
	} else {
		return protocol.MakeIntReply(0)
	}
}

// decr k
func decrCmd(dbPtr *DB, argBytesSlc [][]byte) reply.ReplyItf {
	key := string(argBytesSlc[0])
	return decrAndIncr(dbPtr, key, -1)
}

// decrby k num
func decrByCmd(dbPtr *DB, argBytesSlc [][]byte) reply.ReplyItf {
	key := string(argBytesSlc[0])
	num := util.BytesToInt(argBytesSlc[1])
	return decrAndIncr(dbPtr, key, -num)
}

// incr k
func incrCmd(dbPtr *DB, argBytesSlc [][]byte) reply.ReplyItf {
	key := string(argBytesSlc[0])
	return decrAndIncr(dbPtr, key, 1)
}

// incrby k num
func incrByCmd(dbPtr *DB, argBytesSlc [][]byte) reply.ReplyItf {
	key := string(argBytesSlc[0])
	num := util.BytesToInt(argBytesSlc[1])
	return decrAndIncr(dbPtr, key, num)
}

// decr和decrby和incr和incrby公用方法
func decrAndIncr(dbPtr *DB, key string, num int) reply.ReplyItf {
	value, exist := dbPtr.dictPtr.Get(key)
	var intVal int
	if exist {
		var ok bool
		intVal, ok = value.(int)
		if !ok {
			return protocol.MakeErrReply("ERR value is not an integer or out of range")
		}
		intVal = intVal + num
	} else {
		intVal = +num
	}
	dbPtr.dictPtr.Put(key, intVal)
	return protocol.MakeIntReply(int64(intVal))
}

// string指令注册
func init() {
	RegisterDBCmd("set", setCmd, writeFirstKey, rollbackFirstKey, -3, flagWrite, true)
	RegisterDBCmd("get", getCmd, readFirstKey, rollbackFirstKey, 2, flagRead, false)
}
